package deepr.lib;

import deepr.core.*;
import deepr.entity.DeeprRequest;
import deepr.exception.DeeprException;
import deepr.filter.FilterParser;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class PgQlLib {

  //   where name = 'test2'
  //   limit 2 offset 0
  public static String list(DisposeContext ctx, DeeprRequest deeprRequest) throws Exception {
    deeprRequest.valid();
    List<Map<String, Object>> filter = deeprRequest.getFilter();
    int pageSize = 20;
    deeprRequest.setPageSize(pageSize);
    FilterParser filterParser = new FilterParser(ctx);
    String object = deeprRequest.getObject();
    StringBuilder filterStr = filterParser.parse(object, filter);
    ctx.getDbFilterBehavior().resetDepthObjectFilter();
    String[] select = deeprRequest.getSelect();
    Integer page = deeprRequest.getPage();
    SchemaData schemaData = SchemaManager.getSchemaData(object);
    HashMap<String, PropertyData> fields = schemaData.getFields();
    StringBuilder sb = new StringBuilder();
    String table = schemaData.getName();
    if (deeprRequest.getReadMaster())
      sb.append("/*NO LOAD BALANCE*/");
    sb.append("SELECT COUNT(*) OVER() AS totalCount,").
        append(parseSelectKeys(ctx, fields, object, select)).
        append(" FROM ").append(getTableName(ctx, table)).append("\n");
    FilterLib.appendFilterStr(sb, filterStr);
    sb.append(getPaginateStr(pageSize, page));
    return sb.toString();
  }


  public static String info(DisposeContext ctx, DeeprRequest deeprRequest) throws Exception {
    deeprRequest.valid();
    List<Map<String, Object>> filter = deeprRequest.getFilter();
    FilterParser filterParser = new FilterParser(ctx);
    String object = deeprRequest.getObject();
    StringBuilder filterStr = filterParser.parse(object, filter);
    ctx.getDbFilterBehavior().resetDepthObjectFilter();
    String[] select = deeprRequest.getSelect();
    SchemaData schemaData = SchemaManager.getSchemaData(object);

    HashMap<String, PropertyData> fields = schemaData.getFields();
    StringBuilder sb = new StringBuilder();
    String table = schemaData.getName();
    sb.append("SELECT ").append(parseSelectKeys(ctx, fields, object, select)).append(" FROM ").
        append(getTableName(ctx, table));
    FilterLib.appendFilterStr(sb, filterStr);
    sb.append(" limit 1");
    return sb.toString();
  }

  public static String getTableName(DisposeContext ctx, String table) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
    Class<?> handler = ctx.getSchemaData().getHandler();
    if (handler != null) {
      return (String) handler.getMethod("getTableName",
          DisposeContext.class, String.class).
          invoke(handler.newInstance(), ctx, table);
    }
    return table;
  }


  public static String getTableName(SchemaData schemaData, DisposeContext ctx, String table) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
    Class<?> handler = schemaData.getHandler();
    if (handler != null) {
      return (String) handler.getMethod("getTableName",
          DisposeContext.class, String.class).
          invoke(handler.newInstance(), ctx, table);
    }
    return table;
  }


  public static String getPaginateStr(int pageSize, int page) {
    return "LIMIT " + pageSize + " OFFSET " + (page - 1) * pageSize;
  }


  public static String parseSelectKeys(DisposeContext ctx, HashMap<String, PropertyData> fields, String object, String[] select) throws Exception {
    StringBuilder sb = new StringBuilder();
    for (String s : select) {
      if (sb.length() > 0)
        sb.append(",");
      PropertyData propertyData = fields.get(s);
      if (propertyData == null)
        throw new DeeprException(s + " for " + object + " not exists");
      s = propertyData.getName();
      log.debug("object: {}, field: {}, fieldInfo: {}", object, s, propertyData);
      Class<?> handler = propertyData.getHandler();
      if (handler != null) {
        sb.append(
            handler.getMethod("select",
                DisposeContext.class, String.class, String.class, Object.class).
                invoke(handler.newInstance(), ctx, object, s, propertyData.getHandlerArgs()));
      } else
        sb.append(s);
    }
    return sb.toString();
  }

}
